/*
Common GPSBabel file I/O API
- Copyright (C) 2006 Olaf Klein, o.b.klein@gpsbabel.org
+
+ Copyright (C) 2006,2007,2008 Olaf Klein, o.b.klein@gpsbabel.org
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
*
*/
+
+/*******************************************************************************/
+/* %%% file api wrappers %%% */
+/*******************************************************************************/
+
+#if !ZLIB_INHIBITED
+
+/*******************************************************************************/
+/* %%% Zlib file api %%% */
+/*******************************************************************************/
+
+static gbfile *
+gzapi_open(gbfile *self, const char *mode)
+{
+ char openmode[32];
+
+ self->gzapi = 1;
+
+ /* under non-posix systems files MUST be opened in binary mode */
+
+ strcpy(openmode, mode);
+ if (strchr(mode, 'b') == NULL)
+ strncat(openmode, "b", sizeof(openmode));
+
+ if (self->is_pipe) {
+ FILE *fd;
+ if (self->mode == 'r')
+ fd = stdin;
+ else
+ fd = stdout;
+ SET_BINARY_MODE(fd);
+ self->handle.gz = gzdopen(fileno(fd), openmode);
+ }
+ else
+ self->handle.gz = gzopen(self->name, openmode);
+
+ if (self->handle.gz == NULL) {
+ fatal("%s: Cannot %s file '%s'!\n",
+ self->module,
+ (self->mode == 'r') ? "open" : "create",
+ self->name);
+ }
+
+ return self;
+}
+
+static int
+gzapi_close(gbfile *self)
+{
+ return gzclose(self->handle.gz);
+}
+
+static int
+gzapi_seek(gbfile *self, gbint32 offset, int whence)
+{
+ int result;
+
+ assert(whence != SEEK_END);
+
+ if ((whence == SEEK_CUR) && (self->back != -1)) offset--;
+ result = gzseek(self->handle.gz, offset, whence);
+ self->back = -1;
+
+ if (result < 0) {
+ if (self->is_pipe)
+ fatal("%s: This format cannot be used in piped commands!\n", self->module);
+ fatal("%s: online compression not yet supported for this format!", self->module);
+ }
+ return 0;
+}
+
+static gbsize_t
+gzapi_read(void *buf, const gbsize_t size, const gbsize_t members, gbfile *self)
+{
+ int result = 0;
+ char *target = buf;
+ int count = size * members;
+
+ if (self->back != -1) {
+ *target++ = self->back;
+ count--;
+ result++;
+ self->back = -1;
+ }
+ result += gzread(self->handle.gz, target, count);
+
+ /* Check for an incomplete READ */
+ if ((members == 1) && (size > 1) && (result > 0) && (result < (int)size))
+ fatal("%s: Unexpected end of file (EOF)!\n", self->module);
+
+ result /= size;
+
+ if ((result < 0) || ((gbsize_t)result < members)) {
+ int errnum;
+ const char *errtxt;
+
+ errtxt = gzerror(self->handle.gz, &errnum);
+
+ /* Workaround for zlib bug: buffer error on empty files */
+ if ((errnum == Z_BUF_ERROR) && (gztell(self->handle.gz) == 0)) {
+ return (gbsize_t) 0;
+ }
+ if ((errnum != Z_STREAM_END) && (errnum != 0))
+ fatal("%s: zlib returned error %d ('%s')!\n",
+ self->module, errnum, errtxt);
+ }
+ return (gbsize_t) result;
+}
+
+static gbsize_t
+gzapi_write(const void *buf, const gbsize_t size, const gbsize_t members, gbfile *self)
+{
+ return gzwrite(self->handle.gz, buf, size * members) / size;
+}
+
+static int
+gzapi_flush(gbfile *self)
+{
+ return gzflush(self->handle.gz, Z_SYNC_FLUSH);
+}
+
+static gbsize_t
+gzapi_tell(gbfile *self)
+{
+ gbsize_t result;
+
+ result = gztell(self->handle.gz);
+ if (self->back != -1) result--;
+
+ return result;
+}
+
+static int
+gzapi_eof(gbfile *self)
+{
+ int res = 0;
+
+ if (self->back != -1) return res;
+
+ res = gzeof(self->handle.gz);
+ if (!res) {
+ unsigned char test;
+ int len = gzread(self->handle.gz, &test, 1);
+ if (len == 1) {
+ /* No EOF, put the single byte back into stream */
+ self->back = test;
+ }
+ else {
+ /* we are at the end of the file */
+ if (global_opts.debug_level > 0) {
+ /* now gzeof() should return 1 */
+ is_fatal(!gzeof(self->handle.gz), "zlib gzeof error!\n");
+ }
+ res = 1;
+ }
+ }
+ return res;
+}
+
+static int
+gzapi_ungetc(const int c, gbfile *self)
+{
+ if (self->back == -1)
+ self->back = c;
+ else
+ fatal(MYNAME ": Cannot store more than one byte back!\n");
+ return c;
+}
+
+static void
+gzapi_clearerr(gbfile *self)
+{
+ gzclearerr(self->handle.gz);
+}
+
+static int
+gzapi_error(gbfile *self)
+{
+ int errnum;
+
+ (void)gzerror(self->handle.gz, &errnum);
+
+ return errnum;
+}
+#endif // #if !ZLIB_INHIBITED
+
+
+/*******************************************************************************/
+/* %%% Standard C file api %%% */
+/*******************************************************************************/
+
+static gbfile *
+stdapi_open(gbfile *self, const char *mode)
+{
+ self->handle.std = xfopen(self->name, mode, self->module);
+ return self;
+}
+
+static int
+stdapi_close(gbfile *self)
+{
+ return fclose(self->handle.std);
+}
+
+static int
+stdapi_seek(gbfile *self, gbint32 offset, int whence)
+{
+ int result;
+ gbsize_t pos = 0;
+
+ if (whence != SEEK_SET) pos = ftell(self->handle.std);
+
+ result = fseek(self->handle.std, offset, whence);
+ if (result != 0) {
+ switch (whence) {
+ case SEEK_CUR:
+ case SEEK_END: pos = pos + offset; break;
+ case SEEK_SET: pos = offset; break;
+ default:
+ fatal("%s: Unknown seek operation (%d) for file %s!\n",
+ self->module, whence, self->name);
+ }
+ fatal("%s: Unable to set file (%s) to position (%llu)!\n",
+ self->module, self->name, (long long unsigned) pos);
+ }
+ return 0;
+}
+
+static gbsize_t
+stdapi_read(void *buf, const gbsize_t size, const gbsize_t members, gbfile *self)
+{
+ int errno;
+ gbsize_t result = fread(buf, size, members, self->handle.std);
+
+ if ((result < members) && (errno = ferror(self->handle.std))) {
+ fatal("%s: Error %d occured during read of file '%s'!\n",
+ self->module, errno, self->name);
+ }
+ return result;
+}
+
+static gbsize_t
+stdapi_write(const void *buf, const gbsize_t size, const gbsize_t members, gbfile *self)
+{
+ return fwrite(buf, size, members, self->handle.std);
+}
+
+static int
+stdapi_flush(gbfile *self)
+{
+ return fflush(self->handle.std);
+}
+
+static gbsize_t
+stdapi_tell(gbfile *self)
+{
+ return ftell(self->handle.std);
+}
+
+static int
+stdapi_eof(gbfile *self)
+{
+ return feof(self->handle.std);
+}
+
+static int
+stdapi_ungetc(const int c, gbfile *self)
+{
+ return ungetc(c, self->handle.std);
+}
+
+static void
+stdapi_clearerr(gbfile *self)
+{
+ clearerr(self->handle.std);
+}
+
+static int
+stdapi_error(gbfile *self)
+{
+ return ferror(self->handle.std);
+}
+
+
+/*******************************************************************************/
+/* %%% Memory stream (memapi) %%% */
+/*******************************************************************************/
+
+static gbfile *
+memapi_open(gbfile *self, const char *mode)
+{
+ self->mempos = 0;
+ self->memsz = 0;
+ self->handle.mem = NULL;
+
+ return self;
+}
+
+static int
+memapi_close(gbfile *self)
+{
+ xfree(self->handle.mem);
+
+ return 0;
+}
+
+static int
+memapi_seek(gbfile *self, gbint32 offset, int whence)
+{
+ long long pos = (int)self->mempos;
+
+ switch (whence) {
+ case SEEK_CUR:
+ case SEEK_END: pos = pos + offset; break;
+ case SEEK_SET: pos = offset; break;
+ }
+
+ if ((pos < 0) || (pos > self->memlen)) return -1;
+
+ self->mempos = pos;
+ return 0;
+}
+
+static gbsize_t
+memapi_read(void *buf, const gbsize_t size, const gbsize_t members, gbfile *self)
+{
+ gbsize_t count;
+ gbsize_t result = (self->memlen - self->mempos) / size;
+
+ if (result > members) result = members;
+ count = result * size;
+ if (count) {
+ memcpy(buf, self->handle.mem + self->mempos, count);
+ self->mempos += count;
+ }
+
+ return result;
+}
+
+static gbsize_t
+memapi_write(const void *buf, const gbsize_t size, const gbsize_t members, gbfile *self)
+{
+ gbsize_t count;
+
+ if ((size == 0) && (members == 0)) { /* truncate stream */
+ self->memlen = self->mempos;
+ return 0;
+ }
+
+ count = size * members;
+
+ if (self->mempos + count > self->memsz) {
+ self->memsz = ((self->mempos + count + 4095) / 4096) * 4096;
+ self->handle.mem = xrealloc(self->handle.mem, self->memsz);
+ }
+ memcpy(self->handle.mem + self->mempos, buf, count);
+ self->mempos += count;
+ if (self->mempos > self->memlen) self->memlen = self->mempos;
+
+ return members;
+}
+
+static int
+memapi_flush(gbfile *self)
+{
+ return 0;
+}
+
+static gbsize_t
+memapi_tell(gbfile *self)
+{
+ return self->mempos;
+}
+
+static int
+memapi_eof(gbfile *self)
+{
+ return (self->mempos == self->memlen);
+}
+
+static int
+memapi_ungetc(const int c, gbfile *self)
+{
+ if (self->mempos == 0) return EOF;
+ else {
+ self->mempos--;
+ self->handle.mem[self->mempos] = (unsigned char) c;
+ return c;
+ }
+}
+
+static void
+memapi_clearerr(gbfile *self)
+{
+ return;
+}
+
+static int
+memapi_error(gbfile *self)
+{
+ return 0;
+}
+
+
/* GPSBabel 'file' standard calls */
/*
file = xcalloc(1, sizeof(*file));
- file->name = xstrdup(filename);
file->module = xstrdup(module);
- file->line = xstrdup("");
file->mode = 'r'; // default
file->binary = (strchr(mode, 'b') != NULL);
file->back = -1;
+ file->memapi = (filename == NULL);
for (m = mode; *m; m++) {
switch(tolower(*m)) {
break;
}
}
+
+ if (file->memapi) {
+ file->gzapi = 0;
+ file->name = xstrdup("(Memory stream)");
+
+ file->fileclearerr = memapi_clearerr;
+ file->fileclose = memapi_close;
+ file->fileeof = memapi_eof;
+ file->fileerror = memapi_error;
+ file->fileflush = memapi_flush;
+ file->fileopen = memapi_open;
+ file->fileread = memapi_read;
+ file->fileseek = memapi_seek;
+ file->filetell = memapi_tell;
+ file->fileungetc = memapi_ungetc;
+ file->filewrite = memapi_write;
+ }
+ else {
+ file->name = xstrdup(filename);
+ file->is_pipe = (strcmp(filename, "-") == 0);
- /* Do we have a '.gz' extension in the filename ? */
- len = strlen(file->name);
- if ((len > 3) && (case_ignore_strcmp(&file->name[len-3], ".gz") == 0)) {
+ /* Do we have a '.gz' extension in the filename ? */
+ len = strlen(file->name);
+ if ((len > 3) && (case_ignore_strcmp(&file->name[len-3], ".gz") == 0)) {
#if !ZLIB_INHIBITED
- /* force gzipped files on output */
- file->gzapi = 1;
+ /* force gzipped files on output */
+ file->gzapi = 1;
#else
- fatal(NO_ZLIB);
+ fatal(NO_ZLIB);
#endif
- }
-
- if (file->gzapi) {
-#if !ZLIB_INHIBITED
- char openmode[32];
+ }
- /* under non-posix systems files MUST be opened in binary mode */
-
- strcpy(openmode, mode);
- if (strchr(mode, 'b') == NULL)
- strncat(openmode, "b", sizeof(openmode));
+ if (file->gzapi) {
+#if !ZLIB_INHIBITED
- if (strcmp(filename, "-") == 0) {
- FILE *fd;
- if (file->mode == 'r')
- fd = stdin;
- else
- fd = stdout;
- SET_BINARY_MODE(fd);
- file->handle.gz = gzdopen(fileno(fd), openmode);
- }
- else
- file->handle.gz = gzopen(filename, openmode);
-
- if (file->handle.gz == NULL) {
- fatal("%s: Cannot %s file '%s'!\n",
- module,
- (file->mode == 'r') ? "open" : "create",
- filename);
- }
- file->gzapi = 1;
+ file->fileclearerr = gzapi_clearerr;
+ file->fileclose = gzapi_close;
+ file->fileeof = gzapi_eof;
+ file->fileerror = gzapi_error;
+ file->fileflush = gzapi_flush;
+ file->fileopen = gzapi_open;
+ file->fileread = gzapi_read;
+ file->fileseek = gzapi_seek;
+ file->filetell = gzapi_tell;
+ file->fileungetc = gzapi_ungetc;
+ file->filewrite = gzapi_write;
#else
- /* This is the only runtime test we make */
- fatal("%s: Zlib was not included in this build.\n", file->module);
+ /* This is the only runtime test we make */
+ fatal("%s: Zlib was not included in this build.\n", file->module);
#endif
+ }
+ else {
+ file->fileclearerr = stdapi_clearerr;
+ file->fileclose = stdapi_close;
+ file->fileeof = stdapi_eof;
+ file->fileerror = stdapi_error;
+ file->fileflush = stdapi_flush;
+ file->fileopen = stdapi_open;
+ file->fileread = stdapi_read;
+ file->fileseek = stdapi_seek;
+ file->filetell = stdapi_tell;
+ file->fileungetc = stdapi_ungetc;
+ file->filewrite = stdapi_write;
+ }
}
- else {
- file->handle.std = xfopen(filename, mode, module);
- }
+
+ file->fileopen(file, mode);
+
#ifdef DEBUG_MEM
file->buffsz = 1;
#else
gbfclose(gbfile *file)
{
if (!file) return;
+
+ file->fileclose(file);
- if (file->gzapi) {
-#if !ZLIB_INHIBITED
- gzclose(file->handle.gz);
-#else
- fatal(NO_ZLIB);
-#endif
- }
- else {
- fclose(file->handle.std);
- }
xfree(file->name);
xfree(file->module);
- xfree(file->line);
xfree(file->buff);
xfree(file);
}
gbfread(void *buf, const gbsize_t size, const gbsize_t members, gbfile *file)
{
if ((size == 0) || (members == 0)) return 0;
-
- if (file->gzapi) {
-#if !ZLIB_INHIBITED
- int result = 0;
- char *target = buf;
- int count = size * members;
-
- if (file->back != -1) {
- *target++ = file->back;
- count--;
- result++;
- file->back = -1;
- }
- result += gzread(file->handle.gz, target, count);
-
- /* Check for an incomplete READ */
- if ((members == 1) && (size > 1) && (result > 0) && (result < (int)size))
- fatal("%s: Unexpected end of file (EOF)!\n", file->module);
-
- result /= size;
-
- if ((result < 0) || ((gbsize_t)result < members)) {
- int errnum;
- const char *errtxt;
-
- errtxt = gzerror(file->handle.gz, &errnum);
-
- /* Workaround for zlib bug: buffer error on empty files */
- if ((errnum == Z_BUF_ERROR) && (gztell(file->handle.gz) == 0)) {
- return (gbsize_t) 0;
- }
- if ((errnum != Z_STREAM_END) && (errnum != 0))
- fatal("%s: zlib returned error %d ('%s')!\n",
- file->module, errnum, errtxt);
- }
- return (gbsize_t) result;
-#else
- fatal(NO_ZLIB);
- return -1;
-#endif
- }
- else {
- int errno;
- gbsize_t result = fread(buf, size, members, file->handle.std);
-
- if ((result < members) && (errno = ferror(file->handle.std))) {
- fatal("%s: Error %d occured during read of file '%s'!\n",
- file->module, errno, file->name);
- }
- return result;
- }
+ return file->fileread(buf, size, members, file);
}
/*
{
int result;
- if ((size == 0) || (members == 0)) return 0;
-
- if (file->gzapi) {
-#if !ZLIB_INHIBITED
- result = gzwrite(file->handle.gz, buf, size * members) / size;
-#else
- fatal(NO_ZLIB);
- return -1;
-#endif
- }
- else {
- result = fwrite(buf, size, members, file->handle.std);
- }
-
+ result = file->filewrite(buf, size, members, file);
if (result != members) {
- fatal("%s: Could not write %lld bytes to %s!\n",
+ fatal("%s: Could not write %lld bytes to %s (result %d)!\n",
file->module,
(long long int) (members - result) * size,
- file->name);
+ file->name,
+ result);
}
return result;
int
gbfflush(gbfile *file)
{
- if (file->gzapi) {
-#if !ZLIB_INHIBITED
- return gzflush(file->handle.gz, Z_SYNC_FLUSH);
-#else
- fatal(NO_ZLIB);
- return -1;
-#endif
- }
- else {
- return fflush(file->handle.std);
- }
+ return file->fileflush(file);
}
/*
void
gbfclearerr(gbfile *file)
{
- if (file->gzapi) {
-#if !ZLIB_INHIBITED
- gzclearerr(file->handle.gz);
-#else
- fatal(NO_ZLIB);
-#endif
- }
- else {
- clearerr(file->handle.std);
- }
+ file->fileclearerr(file);
}
/*
int
gbferror(gbfile *file)
{
- int errnum;
-
- if (file->gzapi) {
-#if !ZLIB_INHIBITED
- (void)gzerror(file->handle.gz, &errnum);
-#else
- fatal(NO_ZLIB);
- return -1;
-#endif
- }
- else {
- errnum = ferror(file->handle.std);
- }
- return errnum;
+ return file->fileerror(file);
}
/*
int
gbfseek(gbfile *file, gbint32 offset, int whence)
{
- int result;
-
- if (file->gzapi) {
-
- assert(whence != SEEK_END);
-
-#if !ZLIB_INHIBITED
- if ((whence == SEEK_CUR) && (file->back != -1)) offset--;
- result = gzseek(file->handle.gz, offset, whence);
- file->back = -1;
-#else
- result = 1;
-#endif
- if (result < 0) {
- if (strcmp(file->name, "-") == 0)
- fatal("%s: This format cannot be used in piped commands!\n", file->module);
- fatal("%s: online compression not yet supported for this format!", file->module);
- }
- return 0;
- }
- else {
- gbsize_t pos = 0;
-
- if (whence != SEEK_SET) pos = ftell(file->handle.std);
-
- result = fseek(file->handle.std, offset, whence);
- if (result != 0) {
- switch (whence) {
- case SEEK_CUR:
- case SEEK_END: pos = pos + offset; break;
- case SEEK_SET: pos = offset; break;
- default:
- fatal("%s: Unknown seek operation (%d) for file %s!\n",
- file->module, whence, file->name);
- }
- fatal("%s: Unable to set file (%s) to position (%llu)!\n",
- file->module, file->name, (long long unsigned) pos);
- }
- return 0;
- }
+ return file->fileseek(file, offset, whence);
}
/*
gbsize_t
gbftell(gbfile *file)
{
- gbsize_t result;
-
- if (file->gzapi) {
-#if !ZLIB_INHIBITED
- result = gztell(file->handle.gz);
- if (file->back != -1) {
-// file->back = -1;
- result--;
- }
-#else
- fatal(NO_ZLIB);
- result = -1;
-#endif
- }
- else {
- result = ftell(file->handle.std);
- }
+ gbsize_t result = file->filetell(file);
if ((signed) result == -1)
fatal("%s: Could not determine position of file '%s'!\n",
file->module, file->name);
int
gbfeof(gbfile *file)
{
- if (file->gzapi) {
-#if !ZLIB_INHIBITED
- int res;
-
- if (file->back != -1) return 0;
-
- res = gzeof(file->handle.gz);
- if (!res) {
- unsigned char test;
- int len = gzread(file->handle.gz, &test, 1);
- if (len == 1) {
- /* No EOF, put the single byte back into stream */
- file->back = test;
- }
- else {
- /* we are at the end of the file */
- if (global_opts.debug_level > 0) {
- /* now gzeof() should return 1 */
- is_fatal(!gzeof(file->handle.gz), "zlib gzeof error!\n");
- }
- res = 1;
- }
- }
- return res;
-#else
- fatal(NO_ZLIB);
- return 0;
-#endif
- }
- else {
- return feof(file->handle.std);
- }
+ return file->fileeof(file);
}
/*
int
gbfungetc(const int c, gbfile *file)
{
- int res;
-
- if (file->gzapi) {
-#if !ZLIB_INHIBITED
- if (file->back == -1) {
- file->back = c;
- res = c;
- }
- else {
- fatal(MYNAME ": Cannot store more than one byte back!\n");
- }
-#else
- fatal(NO_ZLIB);
-#endif
- }
- else {
- res = ungetc(c, file->handle.std);
- }
- return res;
+ return file->fileungetc(c, file);
}
/* GPSBabel 'file' enhancements */
gbfgetucs2str(gbfile *file)
{
int len = 0;
- char *result = file->line;
+ char *result = file->buff;
for (;;) {
char buff[8];
clen = cet_ucs4_to_utf8(buff, sizeof(buff), c);
- if (len+clen >= file->linesz) {
- file->linesz += 64;
- result = file->line = xrealloc(file->line, file->linesz + 1);
+ if (len+clen >= file->buffsz) {
+ file->buffsz += 64;
+ result = file->buff = xrealloc(file->buff, file->buffsz + 1);
}
memcpy(&result[len], buff, clen);
len += clen;
gbfgetstr(gbfile *file)
{
int len = 0;
- char *result = file->line;
+ char *result = file->buff;
if (file->unicode) return gbfgetucs2str(file);
file->unicode_checked = 1;
- if (len == file->linesz) {
- file->linesz += 64;
- result = file->line = xrealloc(file->line, file->linesz + 1);
+ if (len == file->buffsz) {
+ file->buffsz += 64;
+ result = file->buff = xrealloc(file->buff, file->buffsz + 1);
}
result[len] = (char)c;
len++;
return (len + 1);
}
+/* Much more higher level functions */
+
+gbsize_t
+gbfcopyfrom(gbfile *file, gbfile *src, gbsize_t count)
+{
+ char buf[1024];
+ gbsize_t copied = 0;
+
+ while (count) {
+ gbsize_t n = gbfread(buf, 1, (count < sizeof(buf)) ? count : sizeof(buf), src);
+ if (n > 0) {
+ gbfwrite(buf, 1, n, file);
+ count -= n;
+ copied += n;
+ }
+ else break;
+ }
+ return copied;
+}
+
/* Thats all, sorry. */